
#ifndef LOGLOGGER_H_
#define LOGLOGGER_H_

#include <atomic>
#include <chrono>
#include <mutex>
#include <sstream>
#include <vector>
#include <algorithm>
#include <chrono>

#include "Logger/LogLevel.h"
#include "Logger/LogEntry.h"


#define MV_CONCATENATE_INNER(lhs, rhs) lhs##rhs

#define MV_CONCATENATE(lhs, rhs) MV_CONCATENATE_INNER(lhs, rhs)

#define MV_GET_LOGGER_FUNCTION_NAME(type) MV_CONCATENATE(MV_CONCATENATE(get, type), Logger)

// If @c MV_LOG_SINK was not defined, default to logging to console.
#ifndef MV_LOG_SINK
#define MV_LOG_SINK Console
#endif

/// Build the get<type>Logger function name for whatever @c Logger logs will be sent to.
#define MV_GET_SINK_LOGGER MV_GET_LOGGER_FUNCTION_NAME(MV_LOG_SINK)


namespace logger {

class Logger {
public:
    /**
     * Logger constructor.
     *
     * @param level The lowest severity level of logs to be emitted by this Logger.
     */
    Logger(Level level);

    /// Destructor.
    virtual ~Logger() = default;

    virtual void setLevel(Level level);

    inline bool shouldLog(Level level) const;

    /**
     * Send a log entry to this Logger.
     *
     * @param level The severity Level to associate with this log entry.
     * @param entry Object used to build the text of this log entry.
     */
    void log(Level level, const LogEntry& entry);

    /**
     * Emit a log entry. Default implementation is no-op.
     *
     * @param level The severity Level of this log line.
     * @param time The time that the event to log occurred.
     * @param threadMoniker Moniker of the thread that generated the event.
     * @param text The text of the entry to log.
     */
    virtual void emit(
        Level level,
        std::chrono::system_clock::time_point time,
        const char* text);

protected:
    /// The lowest severity level of logs to be output by this Logger.
    std::atomic<Level> m_level;
};

bool Logger::shouldLog(Level level) const {
    return level >= m_level;
}


/**
 * Get the @c Logger that logs should be sent to.
 *
 * @return The @c Logger that logs should be sent to.
 */
std::shared_ptr<Logger> MV_GET_SINK_LOGGER();

/**
 * Inline method to get the function that MV_<LEVEL> macros will send logs to.
 * Logs are sent to the @c Logger returned by * @c get<MV_LOG_SINK>Logger().
 */
inline std::shared_ptr<Logger> MV_GET_LOGGER_FUNCTION() {
    static std::shared_ptr<Logger> logger = MV_GET_SINK_LOGGER();
    return logger;
}

}  // namespace logger

#endif  // LOGLOGGER_H_
